package pack.planets;










import java.nio.FloatBuffer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import pack.controls.InGameListener;
import pack.core.Jogo;
import pack.skys.DarkSkyBox;
import pack.skys.DaySkyBox;

import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.input.controls.GameControl;
import com.jme.input.controls.GameControlManager;
import com.jme.input.controls.binding.KeyboardBinding;
import com.jme.input.controls.controller.ActionChangeController;
import com.jme.math.Plane;
import com.jme.math.Vector3f;

import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.RenderPass;

import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Quad;
import com.jme.system.DisplaySystem;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.Timer;

import com.jmex.effects.water.WaterRenderPass;
import com.jmex.physics.util.states.PhysicsGameState;



public class Marinus extends PhysicsGameState
{
	// recursos do display
    private DisplaySystem disp = DisplaySystem.getDisplaySystem();    
    private Camera cam = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
    
    private WaterRenderPass waterEffectRenderPass;
    private Skybox skybox;
    private Quad waterQuad;
    private float farPlane = 10000.0f;
    private float textureScale = 0.02f;
    private InputHandler input;
    
    protected BasicPassManager pManager;

	public Marinus(String name) 
	{
		super(name);		
        
        input = new FirstPersonHandler(cam, 50, 1);        
        
        try {
            GameTaskQueueManager.getManager().update(new Callable<Object>() {
                public Object call() throws Exception {
                    return null;
                }
            }).get();
        } catch (Exception e) {
            e.printStackTrace();
            Jogo.getInstance().quit();
        }
        
        
        skybox = new DarkSkyBox();
        rootNode.attachChild(skybox);
        
        initCam();
        initWater();
        gameListener();        
        
        Callable<Object> render = new Callable<Object>() {
            public Object call() throws Exception {
            	waterEffectRenderPass = new WaterRenderPass(cam, 4, false, true);
                waterEffectRenderPass.setWaterPlane(new Plane(new Vector3f(0.0f, 1.0f,
                        0.0f), 0.0f));
                return null;
            }
        };
        GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER)
                .enqueue(render);        

        
        
        rootNode.updateRenderState();
        rootNode.updateGeometricState(0, true);
        
		
	}
	
	public void gameListener()
	{
        GameControlManager manager = new GameControlManager();
        GameControl exit = manager.addControl("exit");
        exit.addBinding(new KeyboardBinding(KeyInput.KEY_ESCAPE));
        GameControl pause = manager.addControl("pause");
        pause.addBinding(new KeyboardBinding(KeyInput.KEY_P));
        
        ActionChangeController exitAction = new ActionChangeController(exit,
                new InGameListener());
        getRootNode().addController(exitAction);
        ActionChangeController pauseAction = new ActionChangeController(pause,
                new InGameListener());
        getRootNode().addController(pauseAction);		
	}	
	
	private void initCam()
	{
		cam.setFrustumPerspective(45.0f, (float) disp.getWidth()
                / (float) disp.getHeight(), 1f, farPlane);
        cam.setLocation(new Vector3f(-320, 80, -270));
        cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
        cam.update();
	}
	
	private void initWater()
	{
        pManager = new BasicPassManager();

		 Future<WaterRenderPass> future = GameTaskQueueManager.getManager().update(new Callable<WaterRenderPass>() {
	            public WaterRenderPass call() throws Exception {
	            	    cam = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
	            	    waterEffectRenderPass = new WaterRenderPass(cam, 4, false, true);
	                    return waterEffectRenderPass;
	            }
	        });
	        try {
				waterEffectRenderPass = future.get();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		//waterEffectRenderPass = new WaterRenderPass(cam, 6, false, true);
        waterEffectRenderPass.setWaterPlane(new Plane(new Vector3f(0.0f, 1.0f,
                0.0f), 0.0f));
        waterEffectRenderPass.setClipBias(-1.0f);
        waterEffectRenderPass.setReflectionThrottle(0.0f);
        waterEffectRenderPass.setRefractionThrottle(0.0f);

        waterQuad = new Quad("waterQuad", 1, 1);
        FloatBuffer normBuf = waterQuad.getNormalBuffer();
        normBuf.clear();
        normBuf.put(0).put(1).put(0);
        normBuf.put(0).put(1).put(0);
        normBuf.put(0).put(1).put(0);
        normBuf.put(0).put(1).put(0);

        waterEffectRenderPass.setWaterEffectOnSpatial(waterQuad);
        rootNode.attachChild(waterQuad);

        waterEffectRenderPass.setReflectedScene(skybox);
    //    waterEffectRenderPass.addReflectedScene(reflectionTerrain);
        waterEffectRenderPass.setSkybox(skybox);
        pManager.add(waterEffectRenderPass);

        RenderPass rootPass = new RenderPass();
        rootPass.add(rootNode);
        pManager.add(rootPass);
        
        Node fpsNode = new Node("fpsNode");
        RenderPass fpsPass = new RenderPass();
        fpsPass.add(fpsNode);
        pManager.add(fpsPass);
        rootNode.setCullHint(Spatial.CullHint.Never);
        rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        
        //rootNode.setCullMode(SceneElement.CULL_NEVER);		
	}
	

    private void setVertexCoords(float x, float y, float z) {
        FloatBuffer vertBuf = waterQuad.getVertexBuffer();
        vertBuf.clear();

        vertBuf.put(x - farPlane).put(y).put(z - farPlane);
        vertBuf.put(x - farPlane).put(y).put(z + farPlane);
        vertBuf.put(x + farPlane).put(y).put(z + farPlane);
        vertBuf.put(x + farPlane).put(y).put(z - farPlane);
    }

    private void setTextureCoords(int buffer, float x, float y,
            float textureScale) {
        x *= textureScale * 0.5f;
        y *= textureScale * 0.5f;
        textureScale = farPlane * textureScale;
        FloatBuffer texBuf;
        texBuf = waterQuad.getTextureCoords(buffer).coords;
        texBuf.clear();
        texBuf.put(x).put(textureScale + y);
        texBuf.put(x).put(y);
        texBuf.put(textureScale + x).put(y);
        texBuf.put(textureScale + x).put(textureScale + y);
    }
	
    public void update(float f)
    {
    	if (Jogo.getInstance().isPaused()) {
    		f = 0;
    	}
        getPhysicsSpace().update(f);

        pManager.updatePasses(f);
        skybox.getLocalTranslation().set(cam.getLocation());        
        
        input.update(f);
        cam.update();
        
        // agua
        Vector3f transVec = new Vector3f(cam.getLocation().x,
                waterEffectRenderPass.getWaterHeight(), cam.getLocation().z);
        setTextureCoords(0, transVec.x, -transVec.z, textureScale);
        setVertexCoords(transVec.x, transVec.y, transVec.z);
        
        
        rootNode.updateGeometricState(f, true);

    }
    
    public void cleanup() {
        super.cleanup();
        waterEffectRenderPass.cleanup();
    }
    
    public final void setActive(final boolean active) {
        super.setActive(active);
        if (!active) {
            return;
        }
        
        GameTaskQueueManager.getManager().update(new Callable<Object>() {
            public Object call() throws Exception {
                Jogo.getInstance().resume();
                return null;
            }
        });
        
        // desabilita cursor do mouse
        MouseInput.get().setCursorVisible(false);

        
        Timer.getTimer().reset();
        Callable<Object> exe = new Callable<Object>() {
            public Object call() {
                disp.getRenderer().setBackgroundColor(ColorRGBA.black);
                return null;
            }
        };
        GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER)
                .enqueue(exe);
    }    
    
    public void render(float f)
	{
    	if (Jogo.getInstance().isPaused()) {
    		f = 0;
    	}
    	pManager.renderPasses(disp.getRenderer());
    	
		//Clear the screen
		disp.getRenderer().clearBuffers();
		disp.getRenderer().draw(rootNode);
	}
}